# -*- coding: utf-8 -*-
"""
Main driver script for the Dragonfly Aerodynamics Simulation Framework.

This script orchestrates the entire simulation process, from loading the mesh
to running the time-stepping loop and saving the results. It follows the
high-level logic described in the 'report.tex'.
"""

import numpy as np
import logging
import os
import sys
from tqdm import tqdm

# --- Path Setup ---
# Get the absolute path of the directory containing this script (main.py)
# This is the project root for the simulation framework.
PROJECT_ROOT = os.path.dirname(os.path.abspath(__file__))
# Add the project root to the Python path to ensure modules can be found
if PROJECT_ROOT not in sys.path:
    sys.path.append(PROJECT_ROOT)

# Import the configuration and core modules
import config
from core.mesh import Mesh
from core.kinematics import WingKinematics
from core.dynamic_mesh import DynamicMeshManager
from core.solver import CoupledSolver
from core.postprocessing import PostProcessor
from core.visualization import Visualizer

# --- Setup Logging ---
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')

def run_simulation():
    """
    Main function to run the simulation.
    """
    logging.info("--- Starting Dragonfly Aerodynamics Simulation Framework ---")

    # --- 1. Initialization ---
    logging.info("Initializing simulation components...")
    
    try:
        # Construct path to mesh file relative to the project root
        mesh_file_path = os.path.join(PROJECT_ROOT, config.MESH_FILE)
        logging.info(f"Attempting to load mesh from: {mesh_file_path}")

        # Load the computational mesh
        mesh = Mesh(mesh_file_path)

        # Initialize wing kinematics
        kinematics = WingKinematics(config)

        # Initialize the dynamic mesh manager
        dynamic_mesh_manager = DynamicMeshManager(mesh, kinematics, config)

        # Initialize the CFD solver
        solver = CoupledSolver(mesh, config)

        # Initialize the post-processor
        post_processor = PostProcessor(mesh, solver, config)

        # Initialize the visualizer
        visualizer = Visualizer(config)

    except FileNotFoundError:
        logging.error(f"Failed to find mesh file at '{mesh_file_path}'.")
        logging.error("Please ensure the file exists and the path in 'config.py' is correct.")
        return
    except Exception as e:
        logging.error(f"An unexpected error occurred during initialization: {e}")
        return

    # --- 2. Main Time-Stepping Loop ---
    logging.info("Starting main time-stepping loop...")
    
    # Calculate the time steps
    time_steps = np.arange(0, config.SIMULATION_TIME, config.TIME_STEP_SIZE)
    total_steps = len(time_steps)
    
    logging.info(f"Total time steps: {total_steps}")
    logging.info(f"Time step size: {config.TIME_STEP_SIZE:.6f}s")
    logging.info(f"Simulation duration: {config.SIMULATION_TIME}s")
    logging.info(f"Output frequency: every {config.OUTPUT_FREQUENCY} steps")
    
    # Write and visualize the initial state (t=0)
    vtk_path = post_processor.write_output(time_step_index=0)
    if vtk_path:
        visualizer.create_scene(vtk_path, time_step_index=0)
    
    # Use tqdm for a progress bar
    for i, t in enumerate(tqdm(time_steps[1:], desc="Simulating", unit="steps")):
        time_step_index = i + 1
        
        # Only log time steps occasionally to avoid cluttering output
        if time_step_index % 100 == 0:
            logging.info(f"Time Step {time_step_index}/{total_steps}: t = {t:.6f}s")
        else:
            logging.debug(f"Time Step {time_step_index}: t = {t:.6f}s")
        
        # a. Update the mesh to the new wing position
        dynamic_mesh_manager.update(t)
        
        # b. Solve the Navier-Stokes equations for the current time step
        converged = solver.solve_for_timestep()
        
        if not converged:
            logging.error(f"Solver failed to converge at time step {time_step_index}. Aborting simulation.")
            logging.error("This is likely due to the placeholder solver implementation.")
            logging.error("Consider reducing SIMULATION_TIME in config.py for testing.")
            break
            
        # c. Write output data and generate visualization at specified frequency
        if time_step_index % config.OUTPUT_FREQUENCY == 0:
            vtk_path = post_processor.write_output(time_step_index)
            if vtk_path:
                visualizer.create_scene(vtk_path, time_step_index)
            logging.debug(f"Output written for time step {time_step_index}")

    logging.info("--- Simulation Finished ---")
    logging.info(f"Completed {time_step_index if 'time_step_index' in locals() else 0} time steps")


if __name__ == '__main__':
    # To run this script, you need to have a mesh file.
    # For a real run, replace the path in config.py with your GMSH mesh file.
    # We will create a dummy file here if it doesn't exist to allow the script to run.
    dummy_mesh_path = os.path.join(PROJECT_ROOT, config.MESH_FILE)
    if not os.path.exists(dummy_mesh_path):
        import meshio
        logging.warning(f"Mesh file not found. Creating a dummy mesh at: {dummy_mesh_path}")
        mesh_dir = os.path.dirname(dummy_mesh_path)
        if not os.path.exists(mesh_dir):
            os.makedirs(mesh_dir)
        points = [[0, 0, 0], [1, 0, 0], [1, 1, 0], [0, 1, 0]]
        cells = [("triangle", [[0, 1, 2], [0, 2, 3]])]
        meshio.write(dummy_mesh_path, meshio.Mesh(points=points, cells=cells))
    
    run_simulation() 